package com.recharge.domain.context;

import com.recharge.common.util.RedisUtil;
import org.apache.shiro.session.Session;
import org.apache.shiro.session.UnknownSessionException;
import org.apache.shiro.session.mgt.SimpleSession;
import org.apache.shiro.session.mgt.eis.AbstractSessionDAO;
import org.crazycake.shiro.SessionInMemory;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Component;

import java.io.Serializable;
import java.util.*;


/**
 * 重写redis dao
 * 实现redis 存储shiro session时序列化问题
 *
 *
 */
@Component
public class MyRedisSessionDao extends AbstractSessionDAO {

    private static Logger logger = LoggerFactory.getLogger(MyRedisSessionDao.class);

    @Autowired
    RedisUtil redisUtil;


    private String keyPrefix = "shiro:session:";

    private static final long DEFAULT_SESSION_IN_MEMORY_TIMEOUT = 1000L;

    private long sessionInMemoryTimeout = 1000L;

    private static final int DEFAULT_EXPIRE = -2;

    private static final int NO_EXPIRE = -1;

    private int expire = -2;

    private static ThreadLocal sessionsInThread = new ThreadLocal();


    public MyRedisSessionDao() {
    }


    /**
     * 获取session id
     */
    @Override
    protected Serializable doCreate(Session session) {
        if (session == null) {
            logger.error("session is null");
            throw new UnknownSessionException("session is null");
        } else {
            Serializable sessionId = this.generateSessionId(session);
            this.assignSessionId(session, sessionId);
            this.saveSession(session);
            return sessionId;
        }
    }


    /**
     * 获取 session
     *
     */
    @Override
    protected Session doReadSession(Serializable serializable) {
        if (serializable == null) {
            logger.warn("session id is null");
            return null;
        } else {
            Session s = this.getSessionFromThreadLocal(serializable);
            if (s != null) {
                return s;
            } else {
                logger.debug("read session from redis");

                try {
                    String key = keyPrefix + serializable;
                    String str=(String) redisUtil.get(key);
                    Object deserialize = MyRedisSerializer.deserialize(str);
                    SimpleSession simpleSession=(SimpleSession) deserialize;
                    s=simpleSession;
                    this.setSessionToThreadLocal(serializable, s);
                } catch (Exception E) {
                    E.printStackTrace();
                    logger.error("read session error. settionId=" + serializable);
                }
                return s;
            }
        }
    }


    /**
     *
     * 修改session
     */
    @Override
    public void update(Session session) throws UnknownSessionException {
        this.saveSession(session);
    }


    /**
     * 删除session
     */
    @Override
    public void delete(Session session) {
        if(session ==null && session.getId()==null){
            logger.error("session or session id is null");
            throw new UnknownSessionException("session or session id is null");
        }
        String key = keyPrefix + session.getId();
        redisUtil.del(key);
    }


    /**
     * 获取当前活动的session会话
     */
    @Override
    public Collection<Session> getActiveSessions() {
        List<Session> sessions =new ArrayList<>();
        Set keys = redisUtil.keys(keyPrefix);
        Iterator<String> it = keys.iterator();
        while (it.hasNext()) {
            String str = it.next();
            try {
                Object o = redisUtil.get(keyPrefix + str);
                if(o!=null){
                    String value = (String) o;
                    Object deserialize = MyRedisSerializer.deserialize(value);
                    SimpleSession simpleSession=(SimpleSession) deserialize;
                    sessions.add(simpleSession);
                }
            }catch (Exception e){
                e.printStackTrace();
                logger.debug("get all Active session error！！！");
            }

        }
        return sessions;
    }


    /**
     *
     * 保存会话到redis中
     */
    private void saveSession(Session session) throws UnknownSessionException {
        if (session != null && session.getId() != null) {
            String key = keyPrefix + session.getId();

            SimpleSession simpleSession=(SimpleSession)session;
            String serialize = MyRedisSerializer.serialize(simpleSession);

            //System.out.println("1111:"+serialize);
            //session 存在过期时间
            if (this.expire == -2) {
                redisUtil.set(key, serialize, session.getTimeout());
            }

            redisUtil.set(key, serialize);
        }else {
            logger.error("session or session id is null");
            throw new UnknownSessionException("session or session id is null");
        }
    }


    /**
     *
     *
     */
    private void setSessionToThreadLocal(Serializable sessionId, Session s) {
        Map<Serializable, SessionInMemory> sessionMap = (Map)sessionsInThread.get();
        if (sessionMap == null) {
            sessionMap = new HashMap();
            sessionsInThread.set(sessionMap);
        }

        SessionInMemory sessionInMemory = new SessionInMemory();
        sessionInMemory.setCreateTime(new Date());
        sessionInMemory.setSession(s);
        ((Map)sessionMap).put(sessionId, sessionInMemory);
    }

    private Session getSessionFromThreadLocal(Serializable sessionId) {
        Session s = null;
        if (sessionsInThread.get() == null) {
            return null;
        } else {
            Map<Serializable, SessionInMemory> sessionMap = (Map)sessionsInThread.get();
            SessionInMemory sessionInMemory = (SessionInMemory)sessionMap.get(sessionId);
            if (sessionInMemory == null) {
                return null;
            } else {
                Date now = new Date();
                long duration = now.getTime() - sessionInMemory.getCreateTime().getTime();
                if (duration < this.sessionInMemoryTimeout) {
                    s = sessionInMemory.getSession();
                    logger.debug("read session from memory");
                } else {
                    sessionMap.remove(sessionId);
                }

                return s;
            }
        }
    }
}
